#pragma once

#include <userver/concurrent/async_event_channel.hpp>
#include <userver/dynamic_config/impl/snapshot.hpp>
#include <userver/dynamic_config/snapshot.hpp>
#include <userver/engine/shared_mutex.hpp>
#include <userver/rcu/rcu.hpp>
#include <userver/utils/function_ref.hpp>

USERVER_NAMESPACE_BEGIN

namespace dynamic_config::impl {

class StorageData final {
public:
    using SnapshotChannel = concurrent::AsyncEventChannel<const Snapshot&>;
    using DiffChannel = concurrent::AsyncEventChannel<const Diff&>;
    using AfterAssignHook = utils::function_ref<void()>;

    StorageData();
    explicit StorageData(SnapshotData config);

    rcu::ReadablePtr<SnapshotData> Read() const;

    void Update(SnapshotData config, AfterAssignHook after_assign_hook);

    SnapshotChannel& GetChannel();

    DiffChannel& GetDiffChannel();

    concurrent::AsyncEventSubscriberScope DoUpdateAndListen(
        concurrent::FunctionId id,
        std::string_view name,
        SnapshotChannel::Function&& func
    );

    concurrent::AsyncEventSubscriberScope DoUpdateAndListen(
        concurrent::FunctionId id,
        std::string_view name,
        DiffChannel::Function&& func
    );

private:
    Snapshot GetSnapshot() { return Snapshot{*this}; }

    rcu::Variable<SnapshotData> config_;
    SnapshotChannel snapshot_channel_;
    DiffChannel diff_channel_;

    engine::SharedMutex update_mutex_;
};

}  // namespace dynamic_config::impl

USERVER_NAMESPACE_END
